import { Injectable } from '@angular/core';
import { ElectronService } from './electron.service';
import { preset } from '../api/const';
import { formatTime } from '../api/util';
import { SessionService } from './session.service';
import { Observable, Subject } from 'rxjs';

export enum ColorType {
  black = 'black',
  red = 'red',
  green = '#08cc6f',
  yellow = 'yellow',
}
export enum FontType {
  Consolas = 'Consolas',
  arial = 'arial',
  times = 'times',
  courier = 'courier',
  monospace = 'monospace',
  serif = 'serif',
  cursive = 'cursive',
  fantasy = 'fantasy'
}
export interface Setting {
  global: {
    darkMode: boolean
  },
  terminal: {
    rightCopy: boolean,
    closeConfirm: boolean,
    background: ColorType,
    foreground: ColorType,
    fontFamily: FontType,
    fontSize: number,
    fontBold: boolean
  }
}
export interface Host {
  title: string,
  type: "root" | "group" | "host",
  forward?: boolean,
  key?: number,
  children?: Host[],
  ip?: string,
  port?: number,
  user?: string,
  pass?: string,
  winip?: string,
  winport?: number,
  script?: string,
  localPort?: number,
  url?: string,
  parent?: Host,
  child?: Host,
  hopIp?: string,
  group?: string,
  hostnum?: number,
  selected?: boolean,
  oper?: { ssh?: string, sftp?: string, forward?: string }
}
export interface WinForward {
  winip: string,
  winport: number,
  winuser: string,
  winpass: string,
  ip: string,
  port: number,
  comment: string
}

export abstract class ConfigService {
  abstract configFile: string;
  abstract configData: any;

  constructor(protected electron: ElectronService, protected sessionService: SessionService) { }
  abstract configPretreat(data);

  getRealFileName(): string {
    if (!this.electron.isElectron) {
      return this.configFile;
    } else {
      return this.electron.path.join(this.electron.app.getPath('userData'), this.configFile);
    }
  }
  async loadConfig() {
    var strConfig: any;

    strConfig = await this.sessionService.localSession.readFile(this.getRealFileName());
    if (strConfig) this.configData = JSON.parse(strConfig.toString());
    this.configPretreat(this.configData);
  }
  getConfig() {
    return this.configData;
  }
  saveConfig(data = null) {
    if (data == null) data = this.configData;
    this.sessionService.localSession.writeFile(this.getRealFileName(), JSON.stringify(data, null, 4));
  }
}
@Injectable({
  providedIn: 'root'
})
export class HostConfigService extends ConfigService {
  configData: Host;
  protected configChange = new Subject<any>();
  get configChange$(): Observable<string> { return this.configChange }

  constructor(protected electron: ElectronService, protected sessionService: SessionService) {
    super(electron, sessionService);
    if (!this.electron.isElectron) {
      this.configData = preset.hostCfg;
    } else {
      this.configData = this.newHost("root", "ROOT");
    }
  }
  keynum = 0;
  loaded = false;
  configFile = 'ngTermCfg.json';

  isRoot(item: Host) {
    return item && item.type == 'root';
  }
  isGroup(item: Host) {
    return item && (item.type == 'group' || item.type == 'root');
  }
  isHost(item: Host) {
    return item && item.type == 'host';
  }
  isForward(item: Host) {
    return item && (item.type == 'host' && item.forward);
  }
  isNotForward(item: Host) {
    return item && (item.type == 'host' && !item.forward);
  }
  getIconByType(type) {
    switch (type) {
      case 'ssh': return 'code';
      case 'sftp': return 'read';
      case 'local': return 'windows';
      case 'forward': return 'rise';
    }
  }
  sort(hostlist: Host[]) {
    for (let i = 0; i < hostlist.length; i++) {
      for (let j = i + 1; j < hostlist.length; j++) {
        if (hostlist[i].type > hostlist[j].type
          || (hostlist[i].type == hostlist[j].type
            && hostlist[i].title > hostlist[j].title)) {
          let temp = hostlist[i];
          hostlist[i] = hostlist[j];
          hostlist[j] = temp;
        }
      }
    }
  }
  configPretreat(host: Host) {
    if (!host.children)
      host.children = [];
    this.sort(host.children);
    host.children.forEach(subhost => {
      subhost.key = this.keynum++;
      subhost.parent = host;
      this.configPretreat(subhost);
    });
  }

  /** */
  getHostConfig(): any {
    return this.configData;
  }
  getChildGroups(host: Host) {
    let groups = [];
    if (host && host.children) {
      host.children.forEach(item => {
        if (this.isGroup(item)) {
          item.hostnum = this.getChildHosts(item, true, false).length;
          groups.push(item);
        }
      })
    }
    return groups;
  }
  getChildHosts(host: Host, recursive = true, change = true) {
    let _getChildHosts = (host: Host, rootGroup = null, recursive: boolean, change: boolean) => {
      let hosts = [];
      if (host && host.children) {
        host.children.forEach(item => {
          if (this.isHost(item)) {
            if (change) {
              if (this.isHost(host)) item.hopIp = host.ip;
              if (rootGroup) item.group = rootGroup;
            }
            item.hostnum = this.getChildHosts(item, true, false).length;
            hosts.push(item);
          }
          if (recursive) {
            if (rootGroup == null && this.isGroup(item))
              hosts.push.apply(hosts, _getChildHosts(item, item.title, recursive, change));
            else
              hosts.push.apply(hosts, _getChildHosts(item, rootGroup, recursive, change));
          }
        })
      }
      return hosts;
    }
    return _getChildHosts(host, null, recursive, change);
  }
  getHostPath(host: Host) {
    let path = [];
    let tmp = host;
    do {
      path.splice(0, 0, tmp);
      tmp = tmp.parent;
    } while (tmp);
    return path;
  }
  getConnectHost(host: Host, subhost: Host = null): Host {
    if (!host) return null;
    let tmpHost: Host = this.newHost('host');
    this.copyHost(tmpHost, host);
    if (tmpHost.winip) {
      tmpHost.ip = tmpHost.winip;
      tmpHost.port = tmpHost.winport;
    }
    if (subhost) tmpHost.child = subhost;
    if (host.parent && this.isHost(host.parent)) {
      return this.getConnectHost(host.parent, tmpHost);
    } else {
      return tmpHost;
    }
  }
  newHost(type: any, title = '') {
    if (type == 'group' || type == 'root') {
      return {
        type: type,
        key: this.keynum++,
        title: title
      }
    } else {
      return {
        type: type,
        key: this.keynum++,
        title: title,
        forward: false,
        ip: '',
        port: 22,
        user: '',
        pass: '',
        script: '',
        localPort: null,
        url: ''
      }
    }
  }
  copyHost(host1: Host, host2: Host, keepKey = false, withoutKey = false, deepCopy = false) {
    host1.type = host2.type;
    if (!keepKey) host1.key = host2.key;
    host1.title = host2.title;
    if (withoutKey) delete host1.key;
    if (this.isHost(host2)) {
      host1.forward = host2.forward;
      host1.localPort = host2.localPort;
      host1.script = host2.script;
      host1.url = host2.url;
      host1.ip = host2.ip;
      host1.port = host2.port;
      host1.user = host2.user;
      host1.pass = host2.pass;
      delete host1.winip;
      delete host1.winport;
      delete host1.oper;
      if (host2.winip) host1.winip = host2.winip;
      if (host2.winport) host1.winport = host2.winport;
      if (host2.oper) host1.oper = host2.oper;
    }
    if (deepCopy && host2.children && host2.children.length > 0) {
      host1.children = [];
      for (let h of host2.children) {
        let tmpHost: Host = this.newHost(h.type);
        this.copyHost(tmpHost, h, keepKey, withoutKey, deepCopy);
        host1.children.push(tmpHost);
      }
    }
  }
  isChildHost(parent: Host, child: Host) {
    if (!parent.children) return false;
    for (let i = 0; i < parent.children.length; i++) {
      let tmp: Host = parent.children[i];
      if (tmp.key == child.key) {
        return true;
      } else if (this.isChildHost(tmp, child)) {
        return true;
      }
    }
    return false;
  }
  deleteHost(host: Host) {
    let _deleteHost = (baseHost: Host, host: Host) => {
      if (!baseHost.children) return false;
      for (let i = 0; i < baseHost.children.length; i++) {
        let tmp: Host = baseHost.children[i];
        if (tmp.key == host.key) {
          baseHost.children.splice(i, 1);
          this.saveConfig();
          return true;
        } else if (_deleteHost(tmp, host)) {
          return true;
        }
      }
      return false;
    }
    _deleteHost(this.configData, host);
  }
  modifyHost(baseHost: Host, host: Host) {
    let isModify = false;
    if (!baseHost.children) baseHost.children = [];
    for (let h of baseHost.children) {
      if (h.key == host.key) {
        this.copyHost(h, host);
        isModify = true;
      }
    }
    if (!isModify) {
      baseHost.children.push(host);
      host.parent = baseHost;
    }
    this.sort(baseHost.children);
    this.saveConfig();
  }
  getPurgedConfig() {
    let tmpHost: Host = this.newHost('root');
    this.copyHost(tmpHost, this.configData, false, true, true);
    return tmpHost;
  }
  saveConfig() {
    this.configChange.next(this.configData);
    super.saveConfig(this.getPurgedConfig());
  }
  reloadConfig(data) {
    this.configData = JSON.parse(data.toString());
    this.configPretreat(this.configData);
    this.saveConfig();
    return this.configData;
  }

  saveHistory(type: string, host: Host) {
    if (!host) return;
    if (!host.oper) host.oper = {};
    host.oper[type] = formatTime();
    this.saveConfig();
  }
  deleteHistory(type: string, host: Host) {
    delete host.oper[type];
    this.saveConfig();
  }
  getHistory() {
    let history = [];
    let _getHostHistory = (host: Host) => {
      if (host.oper) {
        for (var key in host.oper) {
          history.push({
            optype: key,
            host: host
          });
        }
      }
      if (host.children) {
        host.children.forEach(subhost => {
          _getHostHistory(subhost);
        })
      }
    }
    _getHostHistory(this.configData);
    return history;
  }
}
@Injectable({
  providedIn: 'root'
})
export class SettingService extends ConfigService {
  configData: Setting = {
    global: {
      darkMode: false
    },
    terminal: {
      rightCopy: true,
      closeConfirm: true,
      background: ColorType.black,
      foreground: ColorType.green,
      fontFamily: FontType.Consolas,
      fontSize: 16,
      fontBold: false
    }
  }
  configFile = 'setting.json';
  constructor(protected electron: ElectronService, protected sessionService: SessionService) {
    super(electron, sessionService);
  }
  configPretreat(data: any) { }
}
@Injectable({
  providedIn: 'root'
})
export class WinForwardConfigService extends ConfigService {
  configData = [];
  configFile = 'forward.json';
  constructor(protected electron: ElectronService, protected sessionService: SessionService) {
    super(electron, sessionService);
  }
  setConfig(data) {
    this.configData = data;
  }
  configPretreat(data: any) { }
}